#pragma once

#include <zenox/glm.hpp>
#include <zenoui/Enums.hpp>
#include <zenoui/Rect.hpp>
#include <zenoui/Transform.hpp>

namespace zenoui {

struct UnitedSize {
    float num = 0;
    SizeUnit unit = SizeDontCare;

    constexpr UnitedSize(float num, SizeUnit unit) noexcept : num(num), unit(unit) {
    }

    constexpr UnitedSize(float num) noexcept : UnitedSize(num, SizeInPixels) {
    }

    constexpr UnitedSize() noexcept : UnitedSize(0, SizeDontCare) {
    }

    glm::vec2 eval(float total) const {
        // return x: absolute pixels part, y: stretch factor part
        switch (unit) {
        case SizePercentage:
            return {total * num * 0.01f, 0.0f};
        case SizeStretchFactor:
            return {0.0f, num};
        case SizeInPixels:
            return {num, 0.0f};
        default:
            return {0.0f, 0.0f};
        }
    }
};

inline constexpr UnitedSize operator""_px(long double num) {
    return {static_cast<float>(num), SizeInPixels};
}

inline constexpr UnitedSize operator""_pc(long double num) {
    return {static_cast<float>(num), SizePercentage};
}

inline constexpr UnitedSize operator""_fac(long double num) {
    return {static_cast<float>(num), SizeStretchFactor};
}

inline constexpr UnitedSize operator""_px(unsigned long long num) {
    return {static_cast<float>(num), SizeInPixels};
}

inline constexpr UnitedSize operator""_pc(unsigned long long num) {
    return {static_cast<float>(num), SizePercentage};
}

inline constexpr UnitedSize operator""_fac(unsigned long long num) {
    return {static_cast<float>(num), SizeStretchFactor};
}

struct Shape { // HTML/CSS-like widget styling (margin/padding/size/layout)
    std::array<UnitedSize, 2> size = {1_fac, 1_fac}; // width, height (unit can be pixels, stretch factor, or percentage)
    std::array<std::array<UnitedSize, 2>, 2> margins = {}; // (margin) left, right, top, bottom (not used when parent is AbsoluteLayout)
    std::array<std::array<UnitedSize, 2>, 2> paddings = {}; // (padding) left, right, top, bottom (not used in AbsoluteLayout)
    glm::vec2 position = {0, 0}; // my position offset in parent (px) only work with parent being AbsoluteLayout
    Transform transform = {}; // children uniform transformation, contains translation and scaling for now (used in AbsoluteLayout)
    float zvalue = 0.0f; // widgets with higher Z value will be displayed above other with low Z values
    LayoutMode layout = VerticalLayout; // controls children's layout
    ScissorMode scissor = ScissorBoth; // wheather to cut paint region to the desired widget rect, see Enums.hpp for details about this enum
    /* bool size_is_dynamic = false; // if true, the shape.size is ignored, will dynamically invoke widget->size_hint() instead */
    float event_margin = 0.0f; // additional boundary that can contain children

    glm::vec2 eval_inner_size(size_t axis, Rect const &rect) const {
        float total = rect.m_size[axis];
        return size[axis].eval(total)
            - paddings[axis][0].eval(total)
            - paddings[axis][1].eval(total)
        ;
    }

    glm::vec2 eval_middle_size(size_t axis, Rect const &rect) const {
        float total = rect.m_size[axis];
        return size[axis].eval(total);
    }

    glm::vec2 eval_outer_size(size_t axis, Rect const &rect) const {
        float total = rect.m_size[axis];
        return size[axis].eval(total)
            + margins[axis][0].eval(total)
            + margins[axis][1].eval(total)
        ;
    }
};

}
